package main

import (
	"fmt"
	"io"
	"net/http"
	"net/rpc"
	"net/rpc/jsonrpc"

	"gitee.com/go-course/go8/micro/rpc/json_http/service"
)

// 如何约束HelloService实现 HelloService接口
// var a1 service.HelloService = &HelloService{}
// 多个一个变量a1 --> _
// 多个一个值 &HelloService{} 多个一个开销, nil

// a1.(service.HelloService)
// (*HelloService)(nil) 表示的是一个*HelloService的nil   (int32)(10)  (*int32)(nil)
var _ service.HelloService = (*HelloService)(nil)

type HelloService struct{}

// hello(request)
// Greet函数的签名: 第一个参数Request 是string, 第二个参数是指针
// alice --->
// alice <-- hello alice
func (s *HelloService) Greet(request string, resp *string) error {
	// 往指针内 塞入值
	// *resp = "xxxx"
	// StructA{}, 外出RPC框架层 感知不到该对象的变化(值和指针的区别)
	*resp = fmt.Sprintf("hello, %s", request)
	return nil
}

func NewRPCReadWriteCloserFromHTTP(w http.ResponseWriter, r *http.Request) *RPCReadWriteCloser {
	return &RPCReadWriteCloser{
		Writer:     w,
		ReadCloser: r.Body,
	}
}

type RPCReadWriteCloser struct {
	io.Writer
	io.ReadCloser
}

func main() {
	// 把又给对象的方法暴露成一个RPC到网络上
	// HelloService: RPC网络上 这个服务的名称, 类似于pkg名称
	// &HelloService{}: 提供RPC方法实现的对象(Fn Greet)
	// 客户端类似于这样来调用  HelloService.Greet()
	//
	// &HelloService{}: 的方法是需要约束, 约束了接口方式 Fn(request <T>, response<*T>) error
	rpc.RegisterName("HelloService", &HelloService{})

	// RPC的服务架设在“/jsonrpc”路径，
	// 在处理函数中基于http.ResponseWriter和http.Request类型的参数构造一个io.ReadWriteCloser类型的conn通道。
	// 然后基于conn构建针对服务端的json编码解码器。
	// 最后通过rpc.ServeRequest函数为每次请求处理一次RPC方法调用
	http.HandleFunc("/jsonrpc", func(w http.ResponseWriter, r *http.Request) {
		// reader: Read(p []byte) (n int, err error)
		// writer: Write(p []byte) (n int, err error)
		// closer: Close() error
		// r.Body.Close()

		// http http.ResponseWriter: Write([]byte) (int, error)
		// r.Body : Read(p []byte) (n int, err error)
		// r.Body :Close() error
		// 构造一个HTTP Conn
		conn := NewRPCReadWriteCloserFromHTTP(w, r)
		rpc.ServeRequest(jsonrpc.NewServerCodec(conn))
	})

	err := http.ListenAndServe(":1234", nil)
	if err != nil {
		panic(err)
	}
}
